﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using InfiniTec;
using InfiniTec.Collection.Generic;

namespace ApiScannerShell
{
    public abstract class ViewModelCollection<TViewModel, TModel> : IEnumerable<TViewModel>, INotifyCollectionChanged, IViewModel
        where TViewModel: IViewModel<TModel>
    {
        private readonly ICollection<TModel> _ModelItems;
        private readonly ObservableCollection<TViewModel> _Items = new ObservableCollection<TViewModel>();
        private readonly List<TModel> _AddedItems = new List<TModel>();
        private readonly List<TModel> _RemovedItems = new List<TModel>();
        private readonly List<Func<string>> _ValidationRules = new List<Func<string>>();

        protected ViewModelCollection(ICollection<TModel> modelItems)
        {
            if (modelItems == null) throw new ArgumentNullException("modelItems");
            _ModelItems = modelItems;
            _Items.CollectionChanged += (sender, e) =>
                                            {
                                                if (CollectionChanged != null) CollectionChanged(this, e);
                                            };
        }

        protected void AddValidationRule(Func<string> rule)
        {
            if (rule == null) throw new ArgumentNullException("rule");
            _ValidationRules.Add(rule);
        }

        protected abstract TViewModel CreateViewModel(TModel model);

        protected void ClearItems()
        {
            _RemovedItems.AddRange(_Items.Select(item => item.Model));
            _AddedItems.Clear();
            _Items.Clear();
        }
        protected void AddItem(TViewModel viewModel)
        {
            _Items.Add(viewModel);
            
            var model = viewModel.Model;
            if (_RemovedItems.Contains(model))
            {
                _RemovedItems.Remove(model);
            }
            _AddedItems.Add(model);
        }

        protected void RemoveItem(TViewModel viewModel)
        {
            var item = _Items.FirstOrDefault(element => EqualityComparer<TViewModel>.Default.Equals(element, viewModel));
            if (item != null)
            {
                _Items.Remove(item);
            }
            var model = viewModel.Model;
            _RemovedItems.Add(model);
            if (_AddedItems.Contains(model))
            {
                _AddedItems.Remove(model);
            }
        }
        public IEnumerator<TViewModel> GetEnumerator()
        {
            return _Items.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void SaveValuesToModel()
        {
            foreach (var removedItem in _RemovedItems)
            {
                _ModelItems.Remove(removedItem);
            }
            foreach (var addedItem in _AddedItems)
            {
                _ModelItems.Add(addedItem);
            }
            foreach (var viewModel in _Items)
            {
                viewModel.SaveValuesToModel();
            }
        }


        void IViewModel.LoadValuesFromModel()
        {
            _Items.Clear();
            _Items.AddRange(_ModelItems.Select(CreateViewModel));
            foreach (var viewModel in _Items)
            {
                viewModel.LoadValuesFromModel();
            }
        }

        public IEnumerable<string> GetValidationSummary()
        {
            var errors = from rule in _ValidationRules
                        let result = rule()
                        where result.IsNotNullOrEmpty()
                        select result;

            var itemErrors = from item in this
                         from error in item.GetValidationSummary()
                         select error;
            return errors.Union(itemErrors);
        }

        public void ModelChanged()
        {
            throw new NotImplementedException();
        }

        public event NotifyCollectionChangedEventHandler CollectionChanged;
    }
}